package com.fw4j.jdbc.factory;

import java.io.FileNotFoundException;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;

import com.fw4j.jdbc.annotation.Dao;
import com.fw4j.jdbc.annotation.Query;
import com.fw4j.jdbc.annotation.Where;
import com.fw4j.jdbc.datasource.pool.DataSources;
import com.fw4j.jdbc.entity.ClassInfo;
import com.fw4j.jdbc.entity.MethodInfo;
import com.fw4j.jdbc.exception.InstanceException;
import com.fw4j.jdbc.tools.CreateDao;

public class DaoInstance {
		public Connection conn = null;
		public DaoInstance(){
			this.conn = DataSources.getInstance().getConnection();
		}
		public <T> T getInstance(Class<T> cl) throws InstanceException{
			ClassInfo ci = getClassInfo(cl);
			CreateDao dao = null;
			try{
				dao = new CreateDao();
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}
			Object instance = dao.getInstance(ci.getName());
			if(instance != null){
				try {
					instance.getClass().getMethod("setConnection",java.sql.Connection.class).invoke(instance, conn);
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (InvocationTargetException e) {
					e.printStackTrace();
				} catch (NoSuchMethodException e) {
					e.printStackTrace();
				} catch (SecurityException e) {
					e.printStackTrace();
				}
				return (T)instance;
			}
			dao.createClass(ci.getName(),ci.getExtendsInfo(), ci.getInterfaces(), ci.getDao().value());
			Object[] mis = ci.getMethodInfo();
			for(Object o : mis){
				MethodInfo m = (MethodInfo)o;
				if(m.getName().startsWith("find")){
					createMethod(dao, m);
					dao.callFind(m.getReturnType());
					dao.endMethod();
				}else if(m.getName().startsWith("modify")){
					createMethod(dao, m);
					dao.callModify();
					dao.endMethod();
				}
			}
			instance = dao.loadClass();
			if(instance != null){
				try {
					instance.getClass().getMethod("setConnection",java.sql.Connection.class).invoke(instance, conn);
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (InvocationTargetException e) {
					e.printStackTrace();
				} catch (NoSuchMethodException e) {
					e.printStackTrace();
				} catch (SecurityException e) {
					e.printStackTrace();
				}
				return (T)instance;
			}
			return (T)instance;
		}
	private  void createMethod(CreateDao dao, MethodInfo m) {
		Class[] ptype = m.getParamectersType();
		StringBuffer sbpt = new StringBuffer();
		for(Class c : ptype){
			sbpt.append(c.getName().replace(".", "/"));
			sbpt.append(",");
		}
		sbpt.replace(sbpt.lastIndexOf(","), sbpt.length(), "");
		dao.createMethod(m.getReturnType().getName().replace(".", "/")+" "+m.getName()+" ("+sbpt.toString()+")");
		dao.addList();
		if(m.getWhere().value() != ""){
			dao.addString(m.getQuery().value()+" where "+ m.getWhere().value());
		}else{
			dao.addString(m.getQuery().value());
		}
		for(int i = 0;i<ptype.length;i++){
			Class pt = ptype[i];
			if(pt != Class.class){
				dao.ListAdd(pt,i);
			}
		}//end for
	}
	private  <T> ClassInfo getClassInfo(Class<T> cl){
		ClassInfo classInfo = new ClassInfo();
		Annotation[] ans = cl.getAnnotations();
		for(Annotation a : ans){
			if(a instanceof Dao){
				classInfo.setDao((Dao)a);
			}
		}//end for
		StringBuffer _class_ = new StringBuffer("fw4j_"); 
		_class_.append(cl.getSimpleName());
		String className = _class_.toString();
		classInfo.setName(className);
		classInfo.setExtendsInfo("com/fw4j/jdbc/dao/DaoTemplate");
		classInfo.setInterfaces(new String[]{cl.getName().replace(".","/")});
		classInfo.setMethodInfo(getMethodInfo(cl.getMethods()));
		return classInfo;
	}
	private  Object[] getMethodInfo(Method[] method){
		List<MethodInfo> methodInfoList = new ArrayList<MethodInfo>();
		for(Method m : method){
			MethodInfo methodInfo = new MethodInfo();
			methodInfoList.add(methodInfo);
			Annotation[] ans = m.getAnnotations();
			if(ans != null){
				for(Annotation a : ans){
					if(a instanceof Query){
						methodInfo.setQuery((Query)a);
					}else if(a instanceof Where){
						methodInfo.setWhere((Where)a);
					}//end if
				}//end if
				methodInfo.setReturnType(m.getReturnType());
				methodInfo.setName(m.getName());
				methodInfo.setParamectersType(m.getParameterTypes());
			}//end if
		}//end for
		return methodInfoList.toArray();
	}
}
